import os
import json
import hashlib
from glob import glob
from lisn_utils import GPSDateTime, timedelta, utility, AttrDict

from scripts.plots import plt_mag, plt_delta
from scripts.utils import get_news

from django.shortcuts import render_to_response
from django.contrib.auth.decorators import login_required
from django.template import RequestContext
from django.http import HttpResponse
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.conf import settings

from apps.data.models import Data, DataType, DataDate, DataDownload
from apps.stations.models import Station
from apps.instruments.models import Instrument, InstrumentType

DATE_PATHS = {'gps':'%Y/%m',
              'magnetometer':'%Y/%m',
              'ionosonde':'%Y/%m/%d',
              'products':'%Y/%m/%d',
              }

STATES = (
         (0, 'Registered'),
         (1, 'Ready'),
         (2, 'Running'),
         (3, 'Finished'),
         )

def get_status(n):
    return [tup[1] for tup in STATES if n==tup[0]][0]

def add_attr(iterable, var, value):
    for obj in iterable:
        if isinstance(obj, dict):
            obj[var]=value
        else:
            setattr(obj, var, value) 

def change_key(iterable, old, new):
    for obj in iterable:
        obj[new]=obj[old]
        del obj[old]
    return iterable        

def get_names(iterable, prefix):
    return [s.replace(prefix, '') for s in iterable.keys() if prefix in s]

def show_data(request, instrument=None, datatype=None):
    kwargs = {}
    messages = []
    dtypes = []
    networks = []
    countries = []
    itype = dtype = AttrDict({'alias':None})
    
    if 'daterange' in request.GET:
        dt1, dt2 = [GPSDateTime(s) for s in request.GET['daterange'].split(' - ')]
    elif 'daterange' in request.POST:
        dt1, dt2 = [GPSDateTime(s) for s in request.POST['daterange'].split(' - ')]
    else:
        dt1 = dt2 = GPSDateTime().date() 
    
    chk_networks = AttrDict(check='checked', collapse='')
    chk_countries = AttrDict(check='checked', collapse='')
    chk_stations = AttrDict(check='checked', collapse='in')
    
    itypes = InstrumentType.objects.all()
    
    ###Check for instrument
    if instrument:
        itype = InstrumentType.objects.get(alias=instrument)        
        dtypes = DataType.objects.filter(itype=itype.pk).order_by('name')
    ###Check for datatype
    if datatype:
        st_args = {}
        dtype = DataType.objects.get(alias=datatype)        
        networks = Instrument.objects.filter(data__datatype_id=dtype.pk).distinct().order_by('network').values('network')
        networks = [AttrDict(net) for net in networks]
        add_attr(networks, 'check', 'checked')
        st_args['instrument__data__datatype_id'] = dtype.pk
        nets = [net.network for net in networks]
        nets_in_get = get_names(request.GET, 'N_')
        for net in networks:            
            if net.network not in nets_in_get and len(nets_in_get)>0:
                nets.remove(net.network)
                net.check = ''
                chk_networks.check = ''
                chk_networks.collapse = 'in'
        
        if len(networks)<>len(nets):
            st_args['instrument__network__in'] = nets
        
        countries = Station.objects.filter(**st_args).order_by('country').values('country').distinct()
        countries = [AttrDict(cnt) for cnt in countries]
        add_attr(countries, 'check', 'checked')
        cnts = [cnt.country for cnt in countries]
        cnts_in_get = get_names(request.GET, 'C_')
        for cnt in countries:            
            if cnt.country not in cnts_in_get and len(cnts_in_get)>0:
                cnts.remove(cnt.country)
                cnt.check = ''
                chk_countries.check = ''
                chk_countries.collapse = 'in'
        
    ###Search data
    if 'search' in request.GET and (not instrument or not datatype):
        err = AttrDict()
        err.tag = 'danger'
        err.text = '<strong>Error!</strong> You must choose an instrument and data type'
        messages.append(err) 
    elif 'search' in request.GET:
        dates = [(dt1+timedelta(x)).strftime('%Y-%m-%d') for x in range(int((dt2-dt1)/86400+1))]
    
        stations = DataDate.objects.filter(data__datatype__pk=dtype.pk,
                                           data__instrument__network__in=nets,
                                           data__instrument__station__country__in=cnts,
                                           date__in=dates)
        
        stations = stations.order_by('data__instrument__station__name').values('data__instrument__station__name', 'data__instrument__code').distinct()                
        stations = [AttrDict(site) for site in stations]
        add_attr(stations, 'check', 'checked')
        stations = change_key(stations, 'data__instrument__station__name', 'name')
        stations = change_key(stations, 'data__instrument__code', 'code')    
        
        if len(stations)==0:
            err = AttrDict()
            err.tag = 'warning'
            err.text = '<strong>Warning!</strong> 0 results found'
            messages.append(err)
    
        kwargs['search'] = True
        kwargs['station'] = chk_stations
        kwargs['stations'] = stations        
    
    ###Check for remove item
    if 'remove' in request.POST:
        dd = DataDownload.objects.get(code=request.POST['remove'])
        dd.delete()
        err=AttrDict()
        err.tag = 'success'
        err.text = '<strong>Done!</strong> The items was successfully removed from your cart'
        messages.append(err)
    
    kwargs['dt1'] = dt1.strftime('%Y/%m/%d')
    kwargs['dt2'] = dt2.strftime('%Y/%m/%d')
    kwargs['itypes'] = itypes
    kwargs['itype'] = itype
    kwargs['dtypes'] = dtypes
    kwargs['dtype'] = dtype
    kwargs['network'] = chk_networks
    kwargs['networks'] = networks
    kwargs['country'] = chk_countries
    kwargs['countries'] = countries
    kwargs['messages'] = messages
    kwargs['news'] = get_news(2)
    kwargs.update(cart(request.user.id))
     
    return render_to_response('download.html', kwargs, context_instance=RequestContext(request))

@login_required
def add_data(request, instrument, datatype):
    kwargs = {}
    dt1, dt2 = [GPSDateTime(s) for s in request.POST['daterange'].split(' - ')] 
    chk_networks = AttrDict(check='checked', collapse='')
    chk_countries = AttrDict(check='checked', collapse='')
    chk_stations = AttrDict(check='checked', collapse='in')
    
    itypes = InstrumentType.objects.all().exclude(pk=1)
    itype = InstrumentType.objects.get(alias=instrument)        
    dtypes = DataType.objects.filter(itype=itype.pk).order_by('name')
    
    st_args = {}
 
    dtype = DataType.objects.get(alias=datatype)        
    networks = Instrument.objects.filter(data__datatype_id=dtype.pk).distinct().order_by('network').values('network')
    networks = [AttrDict(net) for net in networks]
    add_attr(networks, 'check', 'checked')
    st_args['instrument__data__datatype_id'] = dtype.pk
    nets = [net.network for net in networks]
    nets_in_get = get_names(request.POST, 'N_')
    for net in networks:            
        if net.network not in nets_in_get and len(nets_in_get)>0:
            nets.remove(net.network)
            net.check = ''
            chk_networks.check = ''
            
    if len(networks)<>len(nets):
        st_args['instrument__network__in'] = nets
    
    countries = Station.objects.filter(**st_args).order_by('country').values('country').distinct()
    countries = [AttrDict(cnt) for cnt in countries]
    add_attr(countries, 'check', 'checked')
    cnts = [cnt.country for cnt in countries]
    cnts_in_get = get_names(request.POST, 'C_')
    for cnt in countries:            
        if cnt.country not in cnts_in_get and len(cnts_in_get)>0:
            cnts.remove(cnt.country)
            cnt.check = ''
            chk_countries.check = ''
    
    dates = [(dt1+timedelta(x)).strftime('%Y-%m-%d') for x in range(int((dt2-dt1)/86400+1))]
    
    stations = DataDate.objects.filter(data__datatype__pk=dtype.pk,
                                       data__instrument__network__in=nets,
                                       data__instrument__station__country__in=cnts,
                                       date__in=dates)
    
    stations = stations.order_by('data__instrument__station__name').values('numfiles',
                                                                           'size',
                                                                           'data__instrument__station__name', 
                                                                           'data__instrument__code').distinct()                
    stations = [AttrDict(site) for site in stations]
    add_attr(stations, 'check', 'checked')
    stations = change_key(stations, 'data__instrument__station__name', 'name')
    stations = change_key(stations, 'data__instrument__code', 'code')
    sites_in_get = get_names(request.POST, 'S_')
    sites_in_get.sort()
    numfiles = 0
    size = 0
    for site in stations:            
        if site.code not in sites_in_get and len(sites_in_get)>0:
            site.check = ''
            chk_stations.check = ''
        else:
            numfiles += site.numfiles
            size += site.size
    
    payload = json.dumps({'datatype':datatype,
                          'instrument':instrument,
                          'dt1':dt1.strftime('%Y-%m-%d'),
                          'dt2':dt2.strftime('%Y-%m-%d'),
                          'stations': sites_in_get})
    
    args = {'user_id': request.user.id,
            'session_id': request.COOKIES['sessionid'],
            'code': hashlib.sha1(payload).hexdigest(),
            'numfiles': numfiles,
            'size': size,
            'info': payload
            }
    
    dd_list = DataDownload.objects.filter(code=args['code'])
    err = AttrDict() 
    if len(dd_list)>0:
        err.tag = 'danger'
        err.text = '<strong>Error!</strong> This data is already in your Cart'
    else:
        dd = DataDownload(**args)
        dd.save()
        err.tag = 'success'
        err.text = '<strong>Done!</strong> The data was successfully added to your Cart'
    
    kwargs['search'] = True
    kwargs['dt1'] = dt1.strftime('%Y/%m/%d')
    kwargs['dt2'] = dt2.strftime('%Y/%m/%d')
    kwargs['itypes'] = itypes
    kwargs['itype'] = itype
    kwargs['dtypes'] = dtypes
    kwargs['dtype'] = dtype
    kwargs['network'] = chk_networks
    kwargs['networks'] = networks    
    kwargs['country'] = chk_countries
    kwargs['countries'] = countries
    kwargs['station'] = chk_stations
    kwargs['stations'] = stations
    kwargs['messages'] = [err]
    kwargs['news'] = get_news(2)
    kwargs.update(cart(request.user.id))
         
    return render_to_response('download.html', kwargs, context_instance=RequestContext(request))

def cart(id):
    ret = {}
    elements = DataDownload.objects.filter(user_id=id, status__lt=3)
    elements = elements.values('info','numfiles','size', 'code', 'status')
    elements = [AttrDict(el) for el in elements]
    ret['total_size'] = utility.human_size(sum([el['size'] for el in elements]))
    for el in elements:
        el.info = json.loads(el.info)
        el.size = utility.human_size(el.size)
        el.datatype = DataType.objects.get(alias=el.info['datatype']).name
        el.status = get_status(el.status)
    ret['cart_list'] = elements
    ret['total_files'] = sum([el['numfiles'] for el in elements])
    return ret

def realtime(request, datatype=None):
    kwargs = {}
    images = []    
    if datatype:
        dtype = DataType.objects.get(alias=datatype)
        kwargs['dtype'] = dtype
        if datatype in ('tec', 'scint', 'PWR'):
            stations = Data.objects.filter(datatype__pk=dtype.pk,
                                           instrument__status=1)
            codes = stations.order_by('instrument__station__name').values('instrument__code')
            codes = [code['instrument__code'] for code in codes \
                     if os.path.exists(settings.REALTIME_PATH+'%s_%s.png' % (code['instrument__code'], datatype))]
            images = [settings.REALTIME_URL+'%s_%s.png' % (code, datatype) for code in codes]
        elif datatype=='minute':
            images = [settings.REALTIME_URL+'mag.png']
        elif datatype=='s4map':
            L = glob(settings.REALTIME_PATH+'s4_map*.png')
            L.sort()
            images = [AttrDict({'img':settings.REALTIME_URL+url.split('/')[-1]}) for url in L]
            add_attr(images, 'active', '')
            images[GPSDateTime().hour*4].active = 'active'
            
            
    kwargs['images'] = images
    kwargs['news'] = get_news(5)
    return render_to_response('realtime.html', kwargs, context_instance=RequestContext(request))

def plot_mag(request):
    date = request.GET.get('date', None)
    code = request.GET.get('code', None)
    scale = request.GET.get('scale', '200')
    img = plt_mag(code=code, date=date, scale=scale, output='web')
    response = HttpResponse(img, content_type='image/png')
    return response
    
def plot_delta(request):
    date = request.GET.get('date', None)
    code = request.GET.get('code', None)
    scale = request.GET.get('scale', '200')
    img = plt_delta(code=code, date=date, scale=scale, output='web')
    response = HttpResponse(img, content_type='image/png')
    return response
    
def update(request, instrument, code, datatype):

    ret = {'error': False, 'code': code, 
           'datatype': datatype, 'date': request.GET['date']}
    dt = GPSDateTime(ret['date'])
       
    try:
        inst = Instrument.objects.get(code=code, 
                                      itype__alias=instrument)
        dtype = DataType.objects.get(alias=datatype)
        dum = Data.objects.filter(instrument__code=code, 
                                  datatype__alias=datatype)
        if len(dum)==1:
            data = dum[0]
        else:
            args = {}
            args['instrument_id'] = inst.pk
            args['datatype_id'] = dtype.pk
            if instrument=='products':
                args['path'] = '/data1/%s/%s/%s' % (instrument, datatype, 
                                                    DATE_PATHS[instrument])
            else:
                args['path'] = '/data1/%s/%s/%s/%s/%s' % (instrument, inst.network, 
                                                          code, DATE_PATHS[instrument], 
                                                          datatype)
            args['status'] = 1
            data = Data(**args)
            data.save()
        dum = DataDate.objects.filter(data_id=data.pk, 
                                      date=dt.datetime)
        if len(dum)==1:
            datadate = dum[0]
            ss, num = utility.path_size(dt.strftime(data.path), 
                                        '*%s*' % dt.strftime(dtype.filename.split(',')[0]))
            setattr(datadate, 'size', ss)
            setattr(datadate, 'numfiles', num)
            ret['size'] = datadate.size
        else:
            args = {}
            args['data_id'] = data.pk
            args['date'] = dt.datetime
            args['size'], args['numfiles'] = utility.path_size(dt.strftime(data.path), 
                                                               '*%s*' % dt.strftime(dtype.filename.split(',')[0]))            
            args['status'] = 1
            datadate = DataDate(**args)            
            ret['size'] = args['size']
        datadate.save()
    except (ObjectDoesNotExist, MultipleObjectsReturned) as e:
        ret['error'] = True
        ret['error_log'] = str(e)
    
    return HttpResponse(json.dumps(ret), content_type="application/json")   
    
    
